package com.request.parse;

import com.rlyy.common.response.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * @author yuanqinglong
 * @since 2021/10/18 11:44
 */
public class RequestParamParser {

    private static final Logger logger = LoggerFactory.getLogger(RequestParamParser.class);

    private static final Set<Class<?>> PARAM_ANNOTATION_TYPES = new HashSet<>();

    static {
        PARAM_ANNOTATION_TYPES.add(RequestParam.class);
        PARAM_ANNOTATION_TYPES.add(RequestBody.class);
    }

    /**
     * 解析接口
     *
     * @param targetInterface 目标接口
     * @throws Exception 异常
     */
    public static void parseInterface(Class<?> targetInterface) throws Exception {
        try {
            doParseInterface(targetInterface);
        } catch (Exception exception) {
            logger.error("解析请求代理异常", exception);
            throw exception;
        }
    }


    /**
     * 解析接口
     *
     * @param targetInterface 目标接口
     */
    public static void doParseInterface(Class<?> targetInterface) throws Exception {
        Method[] declaredMethods = targetInterface.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            if (!Result.class.isAssignableFrom(declaredMethod.getReturnType()) && (Object.class != declaredMethod.getReturnType())) {
                throw new RuntimeException(targetInterface.getName() + "#" + declaredMethod.getName() + "返回类型不是com.rlyy.common.response.Result");
            }
            // 校验方法是否加了RequestMapping注解 指定api地址
            verificationMethod(declaredMethod);
            Parameter[] parameters = declaredMethod.getParameters();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                int paramIndex = i + 1;
                if (parameter.getAnnotations().length == 0) {
                    throw new RuntimeException(targetInterface.getName() + "#" + declaredMethod.getName() + " 第" + paramIndex + "个请求参数缺少注解");
                }
                Annotation[] annotations = parameter.getAnnotations();
                boolean anyMatch = false;
                for (Annotation annotation : annotations) {
                    if (PARAM_ANNOTATION_TYPES.contains(annotation.annotationType())) {
                        anyMatch = true;
                    }
                    Class<?> type = parameter.getType();
                    // RequestBody 只能加在实体类上
                    if (annotation.annotationType() == RequestBody.class) {
                        boolean collection = Collection.class.isAssignableFrom(type);
                        boolean simpleProperty = BeanUtils.isSimpleProperty(type);
                        if (collection || simpleProperty) {
                            throw new RuntimeException(targetInterface.getName() + "#" + declaredMethod.getName() + "(第" + paramIndex + "个参数)不能使用@RequestBody注解、因为此参数类型不是实体对象或Map");
                        }
                    }
                    if (annotation.annotationType() == RequestParam.class) {
                        RequestParam param = (RequestParam) annotation;
                        if (StringUtils.isEmpty(param.value()) && StringUtils.isEmpty(param.name())) {
                            throw new RuntimeException(targetInterface.getName() + "#" + declaredMethod.getName() + "(第" + paramIndex + "个参数)注解未指定参数名称");
                        }
                    }
                }
                if (!anyMatch) {
                    throw new RuntimeException(targetInterface.getName() + "#" + declaredMethod.getName() + "(第" + paramIndex + "个参数)没有匹配的注解解析器");
                }

            }
        }
    }


    /**
     * 验证方法
     *
     * @param declaredMethod 声明方法
     * @throws Exception 异常
     */
    public static void verificationMethod(Method declaredMethod) throws Exception {
        if (!declaredMethod.isAnnotationPresent(RequestMapping.class)) {
            throw new RuntimeException(declaredMethod.getDeclaringClass().getName() + "." + declaredMethod.getName() + ":缺少@RequestMapping注解");
        }
        RequestMapping requestMapping = declaredMethod.getAnnotation(RequestMapping.class);
        if (StringUtils.isEmpty(getRequestMappingValue(requestMapping))) {
            throw new RuntimeException(declaredMethod.getDeclaringClass().getName() + "." + declaredMethod.getName() + " @RequestMapping未输入API");
        }
    }


    /**
     * 得到api
     *
     * @param requestMapping 声明方法
     * @return {@link String}
     */
    public static String getRequestMappingValue(RequestMapping requestMapping) throws Exception {
        if (requestMapping.value().length > 0) {
            return requestMapping.value()[0];
        } else {
            return requestMapping.name();
        }
    }



}
